/** \file    typelib/triangle.h
  * \brief   Planar triangle class with common triangle operations.
  * \date    2003
  * \author  Fedor Chelnokov
  * \version $Header: /home/cscom1r4/cvsroot/cs/include/typelib/triangle.h,v 1.1 2004/09/21 18:21:30 cscom1r4 Exp $
  */

#ifndef typelib_triangle_h
#define typelib_triangle_h

#include "types.h"

namespace types {

template <class T>
struct triangle {
public:
	point<T> v[3]; ///< verteces

public:
	triangle(const point<T> & v0, const point<T> & v1, const point<T> & v2)
		{ v[0] = v0; v[1] = v1; v[2] = v2; }
	template <class U> 
	triangle(const triangle<U> & t)
		{ v[0] = T(t.v[0]); v[1] = T(t.v[1]); v[2] = T(t.v[2]); }
	point<T> & operator [](size_t n)
		{ return v[n]; }
	const point<T> & operator [](size_t n) const
		{ return v[n]; }
	bool operator == (const triangle<T> & t) const
		{ return v[0] == t.v[0] && v[1] == t.v[1] && v[2] == t.v[2]; }
	bool operator != (const triangle<T> & t) const
		{ return !(*this == t); }
	bool operator < (const triangle<T> & t) const ///<lexicographical order
		{ return v[0] < t.v[0] || v[0] == t.v[0] && 
			(v[1] < t.v[1] || v[1] == t.v[1] && v[2] < t.v[2]); }
	triangle<T> & operator = (const triangle<T> & t) 
		{ v[0] = t.v[0]; v[1] = t.v[1]; v[2] = t.v[2]; return * this; }

	///signed doubled value of triangle's area, negative value is returned for not ccw order of verteces
	double double_area() const
		{ return outer_product(v[2]-v[1], v[0]-v[1]); }
	///return true for counter-clockwise following of verteces
	bool ccw() const
		{ return double_area() > 0; }
	triangle<T> & make_ccw()
		{ if (!ccw()) std::swap(v[0], v[1]); return * this; }
	double perimeter() const
		{ return abs(v[0]-v[1]) + abs(v[1]-v[2]) + abs(v[2]-v[0]); }

public:	//complex operatrion
	///return signed height (positive for ccw), dropped from node #n
	double height(int n) const;
	///return projection of node #n on opposite edge
	point<T> projection(int n) const;

	///\return on demand radius and/or center of the inscribed circle
	void incircle(double * radius, point<T> * center) const;
	///\return radius of the inscribed circle, its negative for not ccw order of verteces
	double incircle_radius() const;
	///\return center of the inscribed circle
	point<T> incircle_center() const;

	///\return on demand radius and/or center of the inscribed half-circle with center on edge #e (opposite to node #e)
	void inhalfcircle(int e, double * radius, point<T> * center) const;
	///\return radius of the inscribed half-circle, its negative for not ccw order of verteces
	double inhalfcircle_radius(int e) const;
	///\return center of the inscribed half-circle
	point<T> inhalfcircle_center(int e) const;
};

typedef triangle<unsigned char> byte_triangle;
typedef triangle<short>			short_triangle;
typedef triangle<int>			int_triangle;
typedef triangle<size_t>		size_triangle;
typedef triangle<float>			float_triangle;
typedef triangle<double>		double_triangle;

//implementation

template <class T>
inline double triangle<T>::height(int n) const
{ 
	int n1 = (n+1)%3;
	int n2 = (n+2)%3;
	point<T> AB = v[n2] - v[n1];
	return outer_product(AB, v[n] - v[n1]) / abs(AB);
}

template <class T>
inline point<T> triangle<T>::projection(int n) const
{
	int n1 = (n+1)%3;
	int n2 = (n+2)%3;
	point<T> AB = v[n2] - v[n1];
	return v[n1] + AB * (scalar_product(AB, v[n] - v[n1]) / square(AB));
}

template <class T>
inline void triangle<T>::incircle(double * radius, point<T> * center) const
{
	if (!radius && !center)
		return;
	double d01 = abs(v[0] - v[1]);
	double d02 = abs(v[0] - v[2]);
	double d12 = abs(v[1] - v[2]);
	double h = double_area() / (d01 + d02 + d12);
	if (radius)
		*radius = h;
	if (!center)
		return;
	point<T> t = (v[1] - v[0]) / d01;
	point<T> n(-t.y, t.x);
	*center = v[0] + 0.5*(d01 + d02 - d12)*t + h*n;
}

template <class T>
double triangle<T>::incircle_radius() const
{
	double radius;
	incircle(&radius, 0);
	return radius;
}

template <class T>
point<T> triangle<T>::incircle_center() const
{
	point<T> center;
	incircle(0, &center);
	return center;
}

template <class T>
void triangle<T>::inhalfcircle(int e, double * radius, point<T> * center) const
{
	if (!radius && !center)
		return;
	int e1 = (e+1)%3;
	int e2 = (e+2)%3;
	point<T> AB = v[e2] - v[e1];
	point<T> AC = v[e] - v[e1];
	point<T> BC = v[e] - v[e2];
	double dAC = abs(AC);
	double dBC = abs(BC);
	double r = 1.0 / (dAC + dBC);
	if (radius)
		*radius = outer_product(AB, AC) * r;
	if (center)
		*center = v[e1] + (dAC * r) * AB;
}

template <class T>
double triangle<T>::inhalfcircle_radius(int e) const
{
	double radius;
	inhalfcircle(e, &radius, 0);
	return radius;
}

template <class T>
point<T> triangle<T>::inhalfcircle_center(int e) const
{
	point<T> center;
	inhalfcircle(e, 0, &center);
	return center;
}

} // namespace types

#endif //typelib_triangle_h